package org.subatu.orm.core;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.simple.JdbcClient;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.subatu.orm.annotation.Version;
import org.subatu.orm.entity.EntityBean;
import org.subatu.orm.entity.EntityBeanFactory;
import org.subatu.orm.page.PageHelper;
import org.subatu.orm.page.PageInfo;
import org.subatu.orm.util.ClassUtil;
import org.subatu.orm.util.SqlUtil;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.*;

/**
 * 基础Mapper实现类
 *
 * @param <T>
 */
@Repository("SubatuBaseMapperImpl")
public class BaseMapperImpl<T> implements BaseMapper<T> {

    @Autowired
    private JdbcClient jdbcClient;

    @Override
    public int save(T entity) {
        EntityBean bean = EntityBeanFactory.getBean(entity.getClass());
        String insertSql = bean.getInsertSql();

        // 预先设置ID值
        bean.initIdValues(entity);

        List values = ClassUtil.getAllValues(entity, bean.getAllFieldList());

        KeyHolder keyHolder = new GeneratedKeyHolder();
        JdbcClient.StatementSpec statementSpec = jdbcClient.sql(insertSql).params(values);
        int count = statementSpec.update(keyHolder);

        List<Field> idFieldList = bean.getIdFieldList();
        // 本来可以用循环统一,但一个住建的情况毕竟多,减少循环提升效率
        if (idFieldList.size() == 1) {
            ClassUtil.setFieldValue(entity, idFieldList.get(0), keyHolder.getKey());
        } else if (idFieldList.size() > 1) {
            // 多个id的情况
            Map<String, Object> keys = keyHolder.getKeys();
            for (Field idField : idFieldList) {
                Object idValue = keys.get(SqlUtil.getColumn(idField));
                ClassUtil.setFieldValue(entity, idField, idValue);
            }
        }

        // 设置ID
        return count;
    }

    @Override
    public int deleteById(Serializable id) {
        Class<T> tclazz = (Class<T>) ClassUtil.getTClazz(this.getClass());
        EntityBean bean = EntityBeanFactory.getBean(tclazz);
        String deleteRealSql = bean.getDeleteSql();
        int update = jdbcClient.sql(deleteRealSql).param(1, id).update();
        return update;
    }

    @Override
    public int deleteById(T entity) {
        EntityBean bean = EntityBeanFactory.getBean(entity.getClass());
        String deleteSql = bean.getDeleteSql();
        JdbcClient.StatementSpec statementSpec = jdbcClient.sql(deleteSql);

        // 设置id值
        List<Field> idFieldList = bean.getIdFieldList();
        // 本来可以用循环统一,但一个住建的情况毕竟多,减少循环提升效率
        if (idFieldList.size() == 1) {
            Object idValue = ClassUtil.getFieldValue(entity, idFieldList.get(0));
            statementSpec.param(1, idValue);
        } else if (idFieldList.size() > 1) {
            // 多个id的情况
            int index = 1;
            for (Field idField : idFieldList) {
                Object idValue = ClassUtil.getFieldValue(entity, idField);
                statementSpec.param(index++, idValue);
            }
        }

        return statementSpec.update();
    }

    @Override
    @Transactional
    public int deleteBatchIds(Collection<? extends Serializable> idList) {
        int result = 0;
        for (Serializable id : idList) {
            result += this.deleteById(id);
        }
        return result;
    }

    // 这种情况只有一个id
    @Override
    public int deleteRealById(Serializable id) {
        Class<T> tclazz = (Class<T>) ClassUtil.getTClazz(this.getClass());
        EntityBean bean = EntityBeanFactory.getBean(tclazz);
        String deleteRealSql = bean.getDeleteRealSql();
        int update = jdbcClient.sql(deleteRealSql).param(1, id).update();
        return update;
    }

    @Override
    public int deleteRealById(T entity) {
        EntityBean bean = EntityBeanFactory.getBean(entity.getClass());
        String deleteRealSql = bean.getDeleteRealSql();
        JdbcClient.StatementSpec statementSpec = jdbcClient.sql(deleteRealSql);

        // 设置id值
        List<Field> idFieldList = bean.getIdFieldList();
        // 本来可以用循环统一,但一个住建的情况毕竟多,减少循环提升效率
        if (idFieldList.size() == 1) {
            Object idValue = ClassUtil.getFieldValue(entity, idFieldList.get(0));
            statementSpec.param(1, idValue);
        } else if (idFieldList.size() > 1) {
            // 多个id的情况
            int index = 1;
            for (Field idField : idFieldList) {
                Object idValue = ClassUtil.getFieldValue(entity, idField);
                statementSpec.param(index++, idValue);
            }
        }

        return statementSpec.update();
    }

    @Override
    @Transactional
    public int deleteRealBatchIds(Collection<? extends Serializable> idList) {
        int result = 0;
        for (Serializable id : idList) {
            result += this.deleteRealById(id);
        }
        return result;
    }

    @Override
    public int updateById(T entity) {
        EntityBean bean = EntityBeanFactory.getBean(entity.getClass());
        String updateSql = bean.getUpdateSql();

        JdbcClient.StatementSpec statementSpec = jdbcClient.sql(updateSql);
        List<Field> allUpdatefieldList = bean.getAllUpdateFieldList();
        List values = ClassUtil.getAllValues(entity, allUpdatefieldList);

        int index = 0;
        int version = 0;
        for (Field field : allUpdatefieldList) {
            Object value = values.get(index++);
            if (field.isAnnotationPresent(Version.class) && version == 0) {
                // 第一次版本号加 1
                version = 1;
                value = (int) value + 1;
            }
            statementSpec.param(index, value);
        }
        int count = statementSpec.update();

        return count;
    }

    @Override
    public T getById(Serializable id) {
        Class<T> tclazz = (Class<T>) ClassUtil.getTClazz(this.getClass());
        EntityBean bean = EntityBeanFactory.getBean(tclazz);
        String selectByIdSql = bean.getSelectByIdSql();
        Optional<T> optional = jdbcClient.sql(selectByIdSql).param(1, id).query(tclazz).optional();
        return optional.orElse(null);
    }

    @Override
    public List<T> selectBatchIds(Collection<? extends Serializable> idList) {
        List<T> list = new ArrayList<>();
        for (Serializable id : idList) {
            list.add(this.getById(id));
        }
        return list;
    }

    @Override
    public List<Map<String, Object>> selectMaps() {
        return null;
    }

    @Override
    public List<?> selectList(String sql) {
        return this.selectList(sql, null);
    }

    // 如果有分页, 就内置分页的查
    @Override
    public List<?> selectList(String sql, List<?> params) {
        return this.selectList(sql, params, null);
    }

    @Override
    public List<?> selectList(String sql, List<?> params, Class<?> resultType) {
        if (PageHelper.isNeedPage(sql)) {
            String selectCountSql = SqlUtil.makeSelectCountSql(sql);
            Map<String, Object> map = jdbcClient.sql(selectCountSql).params(params).query().singleRow();
            Long cnt = (Long) map.get("cnt");

            // 设置分页的细节
            PageInfo page = PageHelper.getLocalPage();
            page.setTotalRow(cnt);
            page.setTotalPage((int) (page.getTotalRow() - 1) / page.getPageSize() + 1);
            if (page.getPageNum() > page.getTotalPage()) {
                page.setPageNum(page.getTotalPage());
            }
            if (page.getPageNum() <= 0) {
                page.setPageNum(1);
            }

            // 分页
            sql += " limit :start,:pageSize";
            sql = sql.replace(":start", page.getPageSize() * (page.getPageNum() - 1) + "");
            sql = sql.replace(":pageSize", page.getPageSize() + "");
            logger.debug("分页sql:{}", sql);
        }

        JdbcClient.StatementSpec spec = jdbcClient.sql(sql).params(params);
        List<?> list = null;
        if (resultType != null) {
            list = spec.query(resultType).list();
        } else {
            list = spec.query().listOfRows();
        }
        return list;
    }


}
